package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"go/ast"
	"go/types"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"text/template"
	"unicode"

	"golang.org/x/mod/modfile"
	"golang.org/x/tools/go/packages"
)

func main2() {
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	i := 0
	for {
		_, err = os.Stat(path.Join(wd, "go.mod"))
		if os.IsNotExist(err) {
			wd = path.Dir(wd)
			i++
			if wd == "/" || i == 5 {
				log.Fatalln("Project directory not found")
			}
		} else {
			break
		}
	}
	mpath, err := modulePath(path.Join(wd, "go.mod"))
	if err != nil {
		panic(err)
	}
	internalPath := path.Join(wd, "internal")
	_, err = os.Stat(internalPath)
	if os.IsNotExist(err) {
		log.Fatalln("Internal directory not found")
	}
	packageNames := readPackageList(internalPath, path.Join(mpath, "internal"))
	cmdPackage := readPackageList(path.Join(wd, "cmd"), path.Join(mpath, "cmd"))
	cmdPackage = append(cmdPackage, packageNames...)
	pkgs := readPackages(wd, cmdPackage)
	r := &reader{
		pkgs: pkgs,
	}
	r.readAllPkgNewFunc()
	g := &generate{
		mainFunc:              r.mainFunc,
		packageFuncList:       r.packageFuncList,
		unimplementedPackages: r.unimplementedPackages,
	}
	g.relyOn()
	g.code()
}

// 读取包
func readPackages(wd string, packageNames []string) []*packages.Package {
	cfg := &packages.Config{
		Context: context.Background(),
		Mode:    packages.LoadAllSyntax,
		Dir:     wd,
		Env:     os.Environ(),
	}
	escaped := make([]string, len(packageNames))
	for i := range packageNames {
		escaped[i] = "pattern=" + packageNames[i]
	}
	pkgs, err := packages.Load(cfg, escaped...)

	if err != nil {
		panic(err)
	}
	for _, p := range pkgs {
		for _, e := range p.Errors {
			panic(e)
		}
	}
	return pkgs
}

type generate struct {
	mainFunc              *packageFuncInfo
	packageFuncList       []*packageFunc
	unimplementedPackages map[string]string
}

func (g *generate) relyOn() {
	g.appendRelyOn(g.mainFunc)
	if len(g.mainFunc.RelyOn) == 0 {
		panic("函数" + g.mainFunc.FuncName + "有依赖无法构建")
	}
}
func (g *generate) appendRelyOn(f *packageFuncInfo) {
	yl := f.Params
	inf := func(i string, yl []string) bool {
		for _, v := range yl {
			if v == i {
				return true
			}
		}
		return false
	}
	n := 0
	for _, pList := range g.packageFuncList {
		for _, pf := range pList.FuncList {
			relyOn := false
			if pf.IsMainFunc {
				continue
			}
			for _, result := range pf.Results {
				if inf(result, yl) {
					f.appendRelyOn(pList.FullPackageName, pf) // 条件依赖
					n++
					relyOn = true
				}
			}
			if relyOn && len(pf.Params) > 0 { // 只要有返回值被其他函数依赖，则该函数的参数也要被依赖注入
				g.appendRelyOn(pf)
				if len(pf.RelyOn) == 0 { // 为找到依赖
					panic("函数" + pf.FuncName + "有依赖无法构建")
				}
			}
		}
	}
	if n > len(yl) {
		panic("函数" + f.FuncName + "有依赖存在多个提供者，一个依赖只能有一个提供者")
	}
	if n != len(yl) { // 需要的参数没有找全，放弃依赖
		panic("函数" + f.FuncName + "有依赖无法构建")
	}
}
func (g *generate) code() {
	pStr := &providerSetStr{}
	g.relyondcode(g.mainFunc, pStr)
	wd, _ := os.Getwd()
	writeProviderSet(wd, pStr)
}

func (g *generate) relyondcode(pf *packageFuncInfo, pStr *providerSetStr) {
	if len(pf.RelyOn) == 0 {
		return
	}
	for _, relyon := range pf.RelyOn {
		p, pa, _ := getPackageAlias(relyon.FullPackageName)
		pStr.appendImportPackage(fmt.Sprintf(`%s "%s"`, pa, p))
		for _, rely := range relyon.FuncList {
			if rely.AutoCode { // 函数为自动生成到provider_set.go文件中，依赖注入时不需要加报名
				pStr.appendWireSet(fmt.Sprintf("%s", rely.FuncName))
			} else {
				pStr.appendWireSet(fmt.Sprintf("%s.%s", pa, rely.FuncName))
			}
			if rely.Code != "" {
				for _, re := range rely.Results { // 自动生成的将返回值也导入
					rp, rpa, _ := getPackageAlias(re)
					pStr.appendImportPackage(fmt.Sprintf(`%s "%s"`, rpa, rp))
					rely.Code = strings.ReplaceAll(rely.Code, "{PackageAlias}", rpa)
					for k, para := range rely.Params {
						rp, pat, _ := getPackageAlias(para)
						pStr.appendImportPackage(fmt.Sprintf(`%s "%s"`, pat, rp))
						rely.Code = strings.ReplaceAll(rely.Code, fmt.Sprintf("{PackageAliasP%d}", k+1), pat)
					}
					for k, para := range rely.OtherPackages {
						rp, pat, _ := getPackageAlias(para)
						pStr.appendImportPackage(fmt.Sprintf(`%s "%s"`, pat, rp))
						rely.Code = strings.ReplaceAll(rely.Code, fmt.Sprintf("{OtherPackage%d}", k+1), pat)
					}
				}
				pStr.appendOtherFuncCode(rely.FuncName, rely.Code) // 自动生成的代码
			}
			if rely.IsServerFunc { // 依赖服务，也就是特殊构建的newRegisterGRPCServer函数
				pr, pra, re := getPackageAlias(rely.Results[0])
				pStr.appendImportPackage(fmt.Sprintf(`%s "%s"`, pra, pr))
				pname := strings.Replace(rely.FuncName, "New", "", 1)
				pname = strings.ToLower(pname[:1]) + pname[1:]
				point := ""
				if strings.HasPrefix(rely.Results[0], "*") {
					point = "*"
				}
				pStr.appendInjectRegisterParam(fmt.Sprintf("%s %s%s.%s", pname, point, pra, re))

				pr, pra, _ = getPackageAlias(rely.RegisterServerFunc.packageName)
				pStr.appendImportPackage(fmt.Sprintf(`%s "%s"`, pra, pr))

				pStr.appendRegisterServiceCode(fmt.Sprintf("%s.%s(g, %s)", pra, rely.RegisterServerFunc.registerFunc, pname))
			}
			g.relyondcode(rely, pStr)
		}
	}
}

// 读取方法的参数和返回值
func readFuncParamsAndResults(pkg *packages.Package, f *ast.FuncDecl) (params []string, results []string) {
	sig := pkg.TypesInfo.ObjectOf(f.Name).Type().(*types.Signature)
	reList := sig.Results()
	if reList.Len() == 0 { // 无返回值的方法不被依赖注入
		return nil, nil
	}
	results = make([]string, 0, reList.Len())
	for i := 0; i < reList.Len(); i++ {
		if reList.At(i).Type().String() == "error" {
			continue
		}
		results = append(results, reList.At(i).Type().String())
	}
	if len(results) == 0 { // 只有error返回值，也不进行依赖注入
		return nil, nil
	}
	paList := sig.Params()
	params = make([]string, 0, paList.Len())
	for i := 0; i < paList.Len(); i++ {
		if paList.At(i).Type().String() == "github.com/go-kratos/kratos/v2/log.Logger" { // 特殊接口100%有，不需要处理
			continue
		}
		if paList.At(i).Type().String() == "*gitee.com/shuokeyun/kratos/server.App" { // 特殊接口100%有，不需要处理
			continue
		}
		params = append(params, paList.At(i).Type().String())
	}
	return
}

// 包别名标记
var packageAliasMark map[string]*struct {
	num int
	v   map[string]string
}

// 获取包别名，自动导入的包有可能存在重名
func getPackageAlias(pname string) (string, string, string) {
	if pname == "" {
		return "", "", ""
	}
	if packageAliasMark == nil {
		packageAliasMark = make(map[string]*struct {
			num int
			v   map[string]string
		})
	}
	pname = strings.TrimLeft(pname, "*")
	lastPoint := strings.LastIndex(pname, ".")
	lastG := strings.LastIndex(pname, "/")
	structName := ""
	packageName := pname
	if lastPoint > -1 && lastPoint > lastG {
		structName = pname[lastPoint+1:]
		packageName = pname[:lastPoint]
	}
	minPackageName := packageName[lastG+1:]

	if v, ok := packageAliasMark[minPackageName]; ok {
		if v.v == nil {
			v.v = make(map[string]string)
		}
		if al, ok := v.v[packageName]; ok {
			return packageName, al, structName
		}
		v.v[packageName] = fmt.Sprintf("%s%d", minPackageName, v.num)
		v.num++
		return packageName, v.v[packageName], structName
	}
	packageAliasMark[minPackageName] = &struct {
		num int
		v   map[string]string
	}{
		num: 1,
		v: map[string]string{
			packageName: minPackageName,
		},
	}
	return packageName, minPackageName, structName
}

// 解析模板
func parseTemp(temp string, data interface{}) string {
	tmpl, err := template.New("template").Parse(temp)
	if err != nil {
		panic(err)
	}
	buf := new(bytes.Buffer)
	if err = tmpl.Execute(buf, data); err != nil {
		panic(err)
	}
	s := buf.String()
	return strings.ReplaceAll(s, "(, ", "(")
}

// 读取包名
func readPackageList(dirPath string, mpath string) []string {
	files, err := ioutil.ReadDir(dirPath)
	if err != nil {
		panic(err)
	}
	packageNames := make([]string, 0, 10)
	for _, f := range files {
		if f.IsDir() {
			if strings.Contains(path.Join(mpath, f.Name()), "internal/data/ent") {
				continue
			}
			packageNames = append(packageNames, path.Join(mpath, f.Name()))
			packageNames = append(packageNames, readPackageList(path.Join(dirPath, f.Name()), path.Join(mpath, f.Name()))...)
		}
	}
	return packageNames
}

// 模块路径
func modulePath(filename string) (string, error) {
	modBytes, err := ioutil.ReadFile(filepath.Clean(filename))
	if err != nil {
		return "", err
	}
	return modfile.ModulePath(modBytes), nil
}

type packageFuncInfo struct {
	FuncName           string // 函数名
	IsServerFunc       bool   // 是创建服务的函数
	IsMainFunc         bool   // 是main中的函数
	AutoCode           bool
	Code               string // 函数code,部分函数需要手动注入
	RegisterServerFunc registerServerFunc
	Params             []string       // 参数
	Results            []string       // 返回值
	RelyOn             []*packageFunc // 依赖
	OtherPackages      []string       // 其他需要导入的包，自定义代码内部使用的包
}

func (p *packageFuncInfo) appendRelyOn(packageName string, f *packageFuncInfo) {
	for _, v := range p.RelyOn {
		if v.FullPackageName == packageName {
			for _, vv := range v.FuncList {
				if vv.FuncName == f.FuncName {
					return
				}
			}
			v.FuncList = append(v.FuncList, f)
			return
		}
	}
	p.RelyOn = append(p.RelyOn, &packageFunc{
		FullPackageName: packageName,
		FuncList:        []*packageFuncInfo{f},
	})
}

func (p *packageFuncInfo) clearRelyOn() {
	p.RelyOn = nil
}

func (p *packageFuncInfo) String() string {
	v, _ := json.Marshal(p)
	return string(v)
}

type packageFunc struct {
	FullPackageName string
	FuncList        []*packageFuncInfo
}

type reader struct {
	pkgs                  []*packages.Package
	unimplementedPackages map[string]string
	once                  sync.Once
	packageFuncList       []*packageFunc
	mainFullPackageName   string
	mainFunc              *packageFuncInfo
}

// 读取包中的New开头的方法
func (r *reader) readAllPkgNewFunc() {
	packageFuncList := make([]*packageFunc, 0, len(r.pkgs))
	for _, pkg := range r.pkgs {
		if strings.HasSuffix(pkg.PkgPath, "internal/conf") {
			continue
		}
		pf := &packageFunc{
			FullPackageName: pkg.PkgPath,
		}
		for _, file := range pkg.Syntax {
			for _, decl := range file.Decls {
				if f, ok := decl.(*ast.FuncDecl); ok {
					if pkg.Name == "main" && file.Name.String() == "main" {
						r.mainFullPackageName = pkg.PkgPath
						if f.Name.String() == "withServerOption" {
							params, _ := readFuncParamsAndResults(pkg, f)
							r.mainFunc = &packageFuncInfo{
								FuncName:   f.Name.String(),
								Params:     params,
								IsMainFunc: true,
							}
						}
						continue
					}
					if strings.HasPrefix(f.Name.String(), "New") {
						params, results := readFuncParamsAndResults(pkg, f)
						if results == nil {
							continue
						}
						tmp := &packageFuncInfo{
							FuncName: f.Name.String(),
							Params:   params,
							Results:  results,
						}
						r.tryServerFunc(pkg, f, tmp)
						pf.FuncList = append(pf.FuncList, tmp)
					}
				}
			}
		}
		if len(pf.FuncList) == 0 {
			continue
		}
		packageFuncList = append(packageFuncList, pf)
	}
	r.packageFuncList = packageFuncList
	r.specialMethods()
	r.specialMethodNewRegisterGRPCServer()
	r.specialApiClientMethods()
}

// 构建特殊方法newRegisterGRPCServer，该函数依赖所有的服务
func (r *reader) specialMethodNewRegisterGRPCServer() {
	var params []string
	for _, v := range r.packageFuncList {
		for _, vv := range v.FuncList {
			if vv.IsServerFunc {
				params = append(params, vv.Results[0])
			}
		}
	}
	var fullPackageName string
	for _, pkg := range r.pkgs {
		if !strings.HasSuffix(pkg.PkgPath, "internal/conf") {
			continue
		}
		for _, file := range pkg.Syntax {
			if !strings.HasPrefix(file.Name.String(), "conf") {
				continue
			}
			fullPackageName = pkg.PkgPath
			break
		}
		if fullPackageName != "" {
			break
		}
	}
	entFullPackageName := path.Join(strings.Replace(fullPackageName, "/conf", "", 1), "data", "ent")
	r.packageFuncList = append(r.packageFuncList, &packageFunc{
		FullPackageName: "",
		FuncList: []*packageFuncInfo{
			{
				FuncName: "newRegisterGRPCServer",
				Params:   params,
				AutoCode: true,
				Results:  []string{"gitee.com/shuokeyun/kratos/server.WithAfterGRPCServerFunc"},
			},
			{
				FuncName: "newData",
				AutoCode: true,
				Code:     dataTmp,
				Results:  []string{"*gitee.com/shuokeyun/kratos/data.Data"},
			}, {
				FuncName: "newLogHelper",
				AutoCode: true,
				Code:     helperLogTmp,
				Results:  []string{"*github.com/go-kratos/kratos/v2/log.Helper"},
			}, {
				FuncName: "transformRegistryConf",
				AutoCode: true,
				Code:     transformRegistryConfTemp,
				Results:  []string{"*gitee.com/shuokeyun/apis/pkg/conf.Registry"},
			}, {
				FuncName: "newCache",
				AutoCode: true,
				Code:     cacheTemp,
				Params:   []string{"*" + fullPackageName + ".Bootstrap", "*gitee.com/shuokeyun/kratos/data.Data"},
				Results:  []string{"gitee.com/shuokeyun/common/cache.Cache"},
			}, {
				FuncName:      "newDBClient",
				AutoCode:      true,
				Code:          dBClientTemp,
				Params:        []string{"*" + fullPackageName + ".Bootstrap", "*github.com/go-kratos/kratos/v2/log.Helper"},
				Results:       []string{"*" + entFullPackageName + ".Client"},
				OtherPackages: []string{"entgo.io/ent/dialect/sql", "gitee.com/shuokeyun/common/ent"},
			}, {
				FuncName:      "newRedisClient",
				AutoCode:      true,
				Code:          redisClientTemp,
				Params:        []string{"*" + fullPackageName + ".Bootstrap", "*github.com/go-kratos/kratos/v2/log.Helper"},
				Results:       []string{"*github.com/go-redis/redis/v8.Client"},
				OtherPackages: []string{"context"},
			},
		},
	})
}

// 特殊方法，config中的配置获取
func (r *reader) specialMethods() {
	pfunc := &packageFunc{}
	for _, pkg := range r.pkgs {
		if !strings.HasSuffix(pkg.PkgPath, "internal/conf") {
			continue
		}
		for _, file := range pkg.Syntax {
			if !strings.HasPrefix(file.Name.String(), "conf") {
				continue
			}
			pfunc.FullPackageName = pkg.PkgPath
			for _, decl := range file.Decls {
				if f, ok := decl.(*ast.GenDecl); ok {
					if len(f.Specs) == 0 {
						continue
					}
					if ts, ok := f.Specs[0].(*ast.TypeSpec); ok {
						if ts.Name.Name != "Bootstrap" {
							continue
						}
						pfunc.FuncList = append(pfunc.FuncList, &packageFuncInfo{
							FuncName: "newConfBootstrap",
							AutoCode: true,
							Code:     confBootTmp,
							Results:  []string{"*" + pfunc.FullPackageName + ".Bootstrap"},
						})
						sig := pkg.TypesInfo.ObjectOf(ts.Name).Type().(*types.Named)
						if und, ok := sig.Underlying().(*types.Struct); ok {
							numFields := und.NumFields()
							for i := 0; i < numFields; i++ {
								if !unicode.IsUpper(rune(und.Field(i).Name()[0])) {
									continue
								}
								pfunc.FuncList = append(pfunc.FuncList, &packageFuncInfo{
									FuncName: "newConf" + und.Field(i).Name(),
									AutoCode: true,
									Code: parseTemp(confOneTmp, struct {
										Str string
									}{und.Field(i).Name()}),
									Params:  []string{"*" + pfunc.FullPackageName + ".Bootstrap"},
									Results: []string{"*" + pfunc.FullPackageName + "." + und.Field(i).Name()},
								})
							}
						}
					}
				}
			}
		}
		break
	}
	r.packageFuncList = append(r.packageFuncList, pfunc)
}

// 特殊方法，其他微服务的client依赖
func (r *reader) specialApiClientMethods() {
	//packageFuncMap := make(map[string][]*packageFuncInfo)
	var info []*packageFuncInfo
	for _, v := range r.packageFuncList {
		for _, vv := range v.FuncList {
			for _, p := range vv.Params {
				if strings.HasPrefix(p, "gitee.com/shuokeyun/apis") {
					_, _, strName := getPackageAlias(p)
					/*nfPname, _, _ := getPackageAlias(strPname + "/client")
					packageFuncMap[nfPname] = append(packageFuncMap[nfPname], &packageFuncInfo{
						FuncName: "New" + strName,
						Params:   []string{"*gitee.com/shuokeyun/apis/pkg/conf.Registry"},
						Results:  []string{p},
					})*/
					info = append(info, &packageFuncInfo{
						FuncName: "new" + strName,
						AutoCode: true,
						Code: parseTemp(serverClientTemp, struct {
							Client, Server string
						}{strName, strings.Replace(strName, "Client", "", -1)}),
						Results:       []string{p},
						OtherPackages: []string{"gitee.com/shuokeyun/apis/pkg/conn"},
					})
				}
			}
		}
	}
	if len(info) == 0 {
		return
	}
	for _, v := range r.packageFuncList {
		if v.FullPackageName == "" {
			v.FuncList = append(v.FuncList, info...)
		}
	}
	/*for pname, fl := range packageFuncMap {
		r.packageFuncList = append(r.packageFuncList, &packageFunc{
			FullPackageName: pname,
			FuncList:        fl,
		})
	}*/
}

type registerServerFunc struct {
	packageName  string
	registerFunc string
}

func (r *reader) tryServerFunc(pkg *packages.Package, f *ast.FuncDecl, pf *packageFuncInfo) {
	r.once.Do(func() {
		r.unimplementedPackages = readUnimplementedPackages(r.pkgs)
	})
	sig := pkg.TypesInfo.ObjectOf(f.Name).Type().(*types.Signature)
	reList := sig.Results()
	if reList.Len() == 0 {
		return
	}
	typeFullName := reList.At(0).Type().String()
	lastPointIndex := strings.LastIndex(typeFullName, ".")
	packageName, structName := typeFullName[0:lastPointIndex], typeFullName[lastPointIndex+1:]
	if strings.HasPrefix(packageName, "*") {
		packageName = packageName[1:]
	}
	registerPackage := r.unimplementedPackages[packageName+"."+structName]
	if registerPackage == "" {
		if strings.HasSuffix(structName, "Server") {
			for _, v := range r.unimplementedPackages {
				if v == packageName { // 返回值带Server且包在api中，则返回的是一个server接口，也是一个服务创建函数
					pf.RegisterServerFunc = registerServerFunc{
						packageName:  v,
						registerFunc: "Register" + structName,
					}
					pf.IsServerFunc = true
				}
			}
		}
		return
	}
	pf.RegisterServerFunc = registerServerFunc{
		packageName:  registerPackage,
		registerFunc: "Register" + strings.TrimRight(structName, "Service") + "Server",
	}
	pf.IsServerFunc = true
}

type providerSetStr struct {
	ImportPackage       []string          // 需要导入的包
	WireNewSet          []string          // 需要wire.NewSet的方法
	OtherFuncCode       map[string]string // 其他需要生成的code
	RegisterServiceCode []string
	InjectRegisterParam []string
}

func (p *providerSetStr) appendWireSet(f string) {
	f = strings.TrimSpace(f)
	f = strings.TrimLeft(f, ".")
	for _, v := range p.WireNewSet {
		if v == f {
			return
		}
	}
	p.WireNewSet = append(p.WireNewSet, f)
}

func (p *providerSetStr) appendImportPackage(in string) {
	if in == "" || strings.TrimSpace(in) == `""` {
		return
	}
	for _, v := range p.ImportPackage {
		if v == in {
			return
		}
	}
	if !strings.HasSuffix(in, `"`) {
		in = `"` + in + `"`
	}
	p.ImportPackage = append(p.ImportPackage, in)
}

func (p *providerSetStr) appendOtherFuncCode(name, code string) {
	if p.OtherFuncCode == nil {
		p.OtherFuncCode = make(map[string]string)
	}
	p.OtherFuncCode[name] = code
}

func (p *providerSetStr) appendRegisterServiceCode(in string) {
	for _, v := range p.RegisterServiceCode {
		if v == in {
			return
		}
	}
	p.RegisterServiceCode = append(p.RegisterServiceCode, in)
}

func (p *providerSetStr) appendInjectRegisterParam(in string) {
	for _, v := range p.InjectRegisterParam {
		if v == in {
			return
		}
	}
	p.InjectRegisterParam = append(p.InjectRegisterParam, in)
}

func (p *providerSetStr) haveOtherFuncCode(name string) bool {
	if p.OtherFuncCode == nil {
		return false
	}
	_, ok := p.OtherFuncCode[name]
	return ok
}

func readUnimplementedPackages(pkgs []*packages.Package) map[string]string {
	reply := make(map[string]string)
	for _, pkg := range pkgs {
		for _, file := range pkg.Syntax {
			for _, decl := range file.Decls {
				if f, ok := decl.(*ast.GenDecl); ok {
					if len(f.Specs) == 0 {
						continue
					}
					if ts, ok := f.Specs[0].(*ast.TypeSpec); ok {
						if strings.HasSuffix(ts.Name.String(), "Service") || strings.HasSuffix(ts.Name.String(), "Server") {
							sig := pkg.TypesInfo.ObjectOf(ts.Name).Type().(*types.Named)
							if und, ok := sig.Underlying().(*types.Struct); ok {
								numFields := und.NumFields()
								for i := 0; i < numFields; i++ {
									if strings.HasPrefix(und.Field(0).Name(), "Unimplemented") {
										fieldTypeStr := und.Field(0).Type().String()
										lastPointIndex := strings.LastIndex(fieldTypeStr, ".")
										if lastPointIndex == -1 {
											panic(fmt.Sprintf("%s 无包名", fieldTypeStr))
										}
										reply[sig.String()] = fieldTypeStr[:lastPointIndex]
										break
									}
								}
							}
						}
					}
				}
			}
		}
	}
	return reply
}

func writeProviderSet(dir string, pStr *providerSetStr) {
	tmpl, err := template.New("template").Parse(temp)
	if err != nil {
		panic(err)
	}
	buf := new(bytes.Buffer)
	if err = tmpl.Execute(buf, pStr); err != nil {
		panic(err)
	}
	b := bytes.ReplaceAll(buf.Bytes(), []byte("(, "), []byte("("))
	setFile := filepath.Clean(path.Join(dir, "provider_set.go"))
	err = ioutil.WriteFile(setFile, b, 0644)
	if err != nil {
		panic(err)
	}
	log.Println("generate provider_set.go success")
	cmd := exec.Command("gofmt", []string{"-w", setFile}...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		log.Printf("gofmt provider_set.go fail : %s \n", err.Error())
	} else {
		log.Println("gofmt provider_set.go success")
	}
	cmd = exec.Command("goimports", []string{"-w", setFile}...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		log.Printf("goimports provider_set.go fail : %s \n", err.Error())
	} else {
		log.Println("goimports provider_set.go success")
	}
}

var temp = `//go:generate go run gitee.com/shuokeyun/kratos/cmd/provider-wire@latest

package main

import (
	"github.com/google/wire"
	"github.com/go-kratos/kratos/v2/transport/grpc"

	"gitee.com/shuokeyun/kratos/server"
{{- range.ImportPackage}}
	{{.}}
{{- end}}
)

var providerSet = wire.NewSet(
{{- range .WireNewSet}}
	{{.}},
{{- end}}
)

func newRegisterGRPCServer({{- range .InjectRegisterParam}}, {{.}}{{- end}}) server.WithAfterGRPCServerFunc {
	return func(g *grpc.Server) error {
{{- range .RegisterServiceCode}}
		{{.}}
{{- end}}
		return nil
	}
}

{{- range $key, $value := .OtherFuncCode}}
{{$value}}
{{- end }}
`
var transformRegistryConfTemp = `
func transformRegistryConf(app *server.App) *{PackageAlias}.Registry {
	if app.GetConfigBootstrap().GetRegistry() == nil {
		return nil
	}
	var re {PackageAlias}.Registry
	if app.GetConfigBootstrap().GetRegistry().GetEtcd() != nil {
		re.Etcd = &{PackageAlias}.Registry_Address{
			Address: app.GetConfigBootstrap().GetRegistry().GetEtcd().GetAddress(),
		}
	}
	if app.GetConfigBootstrap().GetRegistry().GetZookeeper() != nil {
		re.Zookeeper = &{PackageAlias}.Registry_Address{
			Address: app.GetConfigBootstrap().GetRegistry().GetZookeeper().GetAddress(),
		}
	}
	return &re
}
`
var dataTmp = `
func newData(app *server.App) (*{PackageAlias}.Data, func(), error) {
	return {PackageAlias}.NewData(app.GetConfigBootstrap(), app.GetLogger())
}
`
var helperLogTmp = `
func newLogHelper(logger {PackageAlias}.Logger) *{PackageAlias}.Helper {
	return {PackageAlias}.NewHelper(logger)
}
`
var confBootTmp = `
func newConfBootstrap(app *server.App) *{PackageAlias}.Bootstrap {
	v, ok := app.GetAppConfigBootstrap().(*{PackageAlias}.Bootstrap)
	if !ok {
		panic("conf error")
	}
	return v
}
`
var confOneTmp = `
func newConf{{.Str}}(conf *{PackageAlias}.Bootstrap) *{PackageAlias}.{{.Str}} {
	return conf.Get{{.Str}}()
}
`
var cacheTemp = `
func newCache(boot *{PackageAliasP1}.Bootstrap, data *{PackageAliasP2}.Data) {PackageAlias}.Cache {
	conf := boot.GetCache()
	c := &{PackageAlias}.Config{
		Store:             conf.GetStore(),
		Prefix:            conf.GetPrefix(),
		DefaultExpiration: conf.GetDefaultExpiration().AsDuration(),
	}
	if conf.GetStore() == "redis" {
		c.RedisClient = data.Redis
	}
	return {PackageAlias}.NewCache(c)
}
`

var serverClientTemp = `
func new{{.Client}}(app *server.App) ({PackageAlias}.{{.Client}}, error) {
	insecure, err := {OtherPackage1}.NewGrpcClientConn(app.GetDiscovery(), {PackageAlias}.{{.Server}}_ServiceDesc.ServiceName, app.GetConfigBootstrap().GetServer().GetName())
	if err != nil {
		return nil, err
	}
	return {PackageAlias}.New{{.Client}}(insecure), nil
}
`

var dBClientTemp = `
func newDBClient(conf *{PackageAliasP1}.Bootstrap, log *{PackageAliasP2}.Helper) (*{PackageAlias}.Client, func(), error) {
	c := conf.GetData().GetDatabase()
	if c == nil || c.GetSource() == "" {
		return nil, nil, nil
	}
	switch c.Driver {
	case dialect.MySQL:
		drv, err := {OtherPackage1}.Open(c.Driver, c.Source)
		if err != nil {
			return nil, nil, err
		}
		db := drv.DB()
		db.SetMaxIdleConns(int(c.MaxIdel))
		db.SetMaxOpenConns(int(c.MaxOpen))
		if c.MaxLifetime.IsValid() {
			db.SetConnMaxLifetime(c.MaxLifetime.AsDuration())
		}
		client := {PackageAlias}.NewClient({PackageAlias}.Driver({OtherPackage2}.NewDriver(drv, log)))
		return client, func() {
			if err := client.Close(); err != nil {
				log.Error(err)
			}
		}, nil
	default:
		return nil, nil, fmt.Errorf("unsupported driver: %q", c.Driver)
	}
}
`

var redisClientTemp = `
func newRedisClient(conf *{PackageAliasP1}.Bootstrap, log *{PackageAliasP2}.Helper) (*{PackageAlias}.Client, func(), error) {
	c := conf.GetData().GetRedis()
	if c == nil || c.GetAddr() == "" {
		return nil, nil, nil
	}
	rd := {PackageAlias}.NewClient(&{PackageAlias}.Options{
		Network:      c.GetNetwork(),
		Addr:         c.GetAddr(),
		Password:     c.GetPassword(),
		DB:           int(c.GetDb()),
		ReadTimeout:  c.GetReadTimeout().AsDuration(),
		WriteTimeout: c.GetWriteTimeout().AsDuration(),
		PoolSize:     int(c.GetPoolSize()),
		IdleTimeout:  c.GetIdleTimeout().AsDuration(),
	})
	if err := rd.Ping({OtherPackage1}.Background()).Err(); err != nil {
		return nil, nil, err
	}
	return rd, func() {
		err := rd.Close()
		if err != nil {
			log.Errorf("closing the redis resources fail, err = %s", err.Error())
		} else {
			log.Info("closing the redis resources")
		}
	}, nil
}
`
